對於 PHP Stream 的概念網路上的資源甚少,儘管這是從 PHP 4.3 開始就存在的功能,卻極少見到有開發者會去使用這樣的 Feature(或是使用這樣的 Feature 但不自知)。
先定義「Stream」是怎麼樣的存在,有助於理解它的意義:
起點與終點可以允許為
詳細的列表可以參閱官方文件
事實上,我們時常會時用到 Stream,只是通常不自知。
舉例而言,file_get_content()
函式中所要求的參數就是 Stream,我們可以在裡面自由填入 http://
或 file://
(如果沒有填的話預設是 file://
)以取得資源。
許多與「檔案操作」類似的函式也都是以流為基本型式:fopen()
, fclose()
, fgets()
, fwrite()
, fseek()
。
值得一提的是,include
及 require
也會受到 stream 的影響,所以如果系統存在 LFI 的話是相當危險的(關於 LFI 與 Stream 我會再開一篇文章討論)
註:PHP 預設會將
allow_url_include
關閉,所以通常無法直接 include 一個遠端的 PHP 檔案造成 RCE。
php://
在 PHP 中內建了一系列的 php://
的 Stream Protocal,可以利用這些 protocol 強化 PHP 的某些功能。
取得所有的 input 通常來源於 HTTP body,值得注意的是,由這個 Stream 取得的內容是 Raw Body,所以需要自行解析。
取得由標準輸入所進入的內容,類似於 C++ 中的 cin
,通常用於取得 Command Line 時的輸入。
將內容輸出至標準輸出中,類似於 C++ 的 cout
。值得注意的是它只能寫不能讀(也無法用 fseek()
尋址)
將內容輸出至記憶體中,可用於測試時期或一些快取資料。值得注意的是記憶體是有限的,所以如果記憶體耗盡可能會產生無法開啟的錯誤。
與 php://memory
相似,優先會將資料存入記憶體,但若記憶體空間不足會把資料寫進臨時檔案。
PHP 存在一些超全域變數,然而隨著 HTTP 的演進,卻遲遲沒有增加 $_PUT
或 $_PATCH
等變數。
即便到了 2019 的今天,如果要取得 PUT 或 PATCH method 的 request body,仍然要透過 Stream。
<?php
$body = file_get_contents('php://input');
// 在這邊拿到的 $body 會是 raw http body(如下所示),需要自行解析它
// $body = 'name=Vincent&age=25';
甚至如果要做到「用 PUT Method 上傳檔案」,也是用類似的做法(但是要注意的是 enctype=multipart/form-data
的解析問題)
PHP 官方文件中有對此進行解釋,但事實上它會無法使用如 $_FILES
等常用的上傳檔案處理方式。
之後 Stream 應該會再開兩篇文章,說明如何建立自定義的 Stream 及如何使用 Filter。